<template>
  <Upload
    :file-list="fileList"
    list-type="picture-card"
    :show-upload-list="true"
    :before-upload="beforeUpload"
    :custom-request="customRequest"
    :accept="accept"
    :maxCount="maxCount"
    @preview="handlePreview"
    @change="handleChange"
  >
    <div class="bo-upload_wrap" :style="style">
      <plus-outlined />
      <div style="margin-top: 8px">上传图片</div>
    </div>
  </Upload>
  <div style="display: none">
    <ImagePreviewGroup
      :preview="{
        visible,
        current,
        onVisibleChange: (vis) => (visible = vis),
      }"
    >
      <Image
        v-for="(item, index) in imgList"
        :key="index"
        :src="item"
        :style="style"
      >
      </Image>
    </ImagePreviewGroup>
  </div>
</template>
<script setup lang="tsx">
import { computed, PropType, ref, CSSProperties } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import {
  Form,
  Image,
  ImagePreviewGroup,
  Modal,
  Upload,
  UploadChangeParam,
  UploadFile,
  type UploadProps,
  message,
} from 'ant-design-vue';
import { AxiosRequestConfig } from 'axios';

const formItemContext = Form.useInjectFormItemContext();

const imgUrl = defineModel<string>('value', {
  default: undefined,
});
const props = defineProps({
  request: {
    type: Function as PropType<
      (
        data: FormData,
        config?: AxiosRequestConfig,
      ) => Promise<{ data: { url: string } }>
    >,
    required: true,
  },
  maxCount: {
    type: Number,
    default: 1,
  },
  accept: {
    type: String,
    default: 'image/jpeg, image/png',
  },
  size: {
    type: Number,
    default: 2,
  },
  height: {
    type: Number,
    default: 100,
  },
  width: {
    type: Number,
    default: 100,
  },
});

const style = computed<CSSProperties>(() => {
  return {
    width: `${props.width}px`,
    height: `${props.height}px`,
  };
});

const current = ref(0);
const visible = ref(false);
const imgList = computed(() => {
  return imgUrl.value ? imgUrl.value.split(',') : [];
});

const fileList = computed<any>(() => {
  return imgList.value.map((url) => ({ url }));
});

const beforeUpload = (
  file: Exclude<UploadProps['fileList'], undefined>[number],
) => {
  const isAccept = props.accept.includes(file.type!);
  if (!isAccept) {
    message.error(`请上传${props.accept}格式图片!`);
  }
  const isLtSize = file.size! / 1024 / 1024 < props.size;
  if (!isLtSize) {
    message.error(`图片最大支持${props.size}MB!`);
  }
  return isAccept && isLtSize;
};

const customRequest: UploadProps['customRequest'] = ({
  file,
  filename,
  onError,
  onSuccess,
  onProgress,
}) => {
  if (imgList.value.length > props.maxCount)
    return message.error('最多上传' + props.maxCount + '张图片');
  const formData = new FormData();
  // @ts-ignore
  formData.append('file', file as Blob, file.name);
  // @ts-ignore
  formData.append('fileName', file.name as string);
  props
    .request(formData, {
      onUploadProgress: onProgress,
    })
    .then((success) => {
      let urlList = imgUrl.value
        ? [...imgList.value, success.data.url]
        : [success.data.url];
      imgUrl.value = urlList.join();
      formItemContext.onFieldChange();
    })
    .catch((err: any) => {
      Modal.error({
        title: '提示',
        content: err.message,
      });
      onError?.(err);
    });
};

const handlePreview = async (
  file: Exclude<UploadProps['fileList'], undefined>[number],
) => {
  current.value = fileList.value.findIndex(
    (item: { url: string | undefined }) => item.url === file.url,
  );
  visible.value = true;
};
const handleChange: UploadProps['onChange'] = (
  info: UploadChangeParam<UploadFile<unknown>>,
) => {
  if (info.file.status === 'removed') {
    const index = fileList.value.findIndex(
      (item: { url: any }) => item.url === (info as any).url,
    );
    imgList.value.splice(index, 1);
    imgUrl.value = imgList.value.join();
    formItemContext.onFieldChange();
  }
};
</script>
<style lang="scss">
.bo-upload {
  &_wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: inherit;
  }
}
</style>
